package com.csw.android.androidtest.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.SweepGradient;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.Nullable;

import com.csw.android.dev_utils.utils.ScreenInfo;

public class CircleSeekBar extends View {
    private static final int DEFAULT_SIZE = ScreenInfo.INSTANCE.dp2Px(80);
    private final RectF drawableArea = new RectF();//可绘制区域
    private final RectF ringDrawRectF = new RectF();//环形绘制区域
    private final Paint ringBackgroundPaint = new Paint();//环形背景画笔
    private final Paint progressPaint = new Paint();//进度画笔
    private final Paint commonPaint = new Paint();//通用画笔
    private int ringBackgroundColor = Color.rgb(228, 228, 228);//环形背景颜色
    private int progressStartColor = Color.rgb(247, 156, 156);//进度条起始位置颜色
    private int progressEndColor = Color.rgb(247, 45, 100);//进度条结束位置颜色
    private int max = 360;//最大刻度
    private int progress = 0;//当前刻度
    private int ringWidth = ScreenInfo.INSTANCE.dp2Px(20);//圆环宽度
    private final Path progressPath = new Path();
    private final Path progressStartPath = new Path();
    private final Path progressEndPath = new Path();

    private float ringRadius = 0;
    private OnSeekBarChangeListener mOnSeekBarChangeListener = null;

    private int sliderColor = Color.WHITE;//滑块颜色
    private int sliderSize = ScreenInfo.INSTANCE.dp2Px(15);//滑块尺寸

    public CircleSeekBar(Context context) {
        super(context);
        init();
    }

    public CircleSeekBar(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public CircleSeekBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        ringBackgroundPaint.setAntiAlias(true);
        ringBackgroundPaint.setColor(ringBackgroundColor);
        ringBackgroundPaint.setStyle(Paint.Style.STROKE);
        ringBackgroundPaint.setStrokeWidth(ringWidth);
        ringBackgroundPaint.setStrokeCap(Paint.Cap.ROUND);

        progressPaint.setAntiAlias(true);
        progressPaint.setStyle(Paint.Style.STROKE);
        progressPaint.setStrokeWidth(ringWidth);
        progressPaint.setStrokeCap(Paint.Cap.BUTT);

        commonPaint.setAntiAlias(true);
        commonPaint.setStyle(Paint.Style.FILL);
    }

    /**
     * 覆写测量方法，根据当前容器宽高和默认宽高，来决定自己的尺寸
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        int width = widthSize;
        int height = heightSize;

        int heightContent = heightSize - getPaddingTop() - getPaddingBottom();
        int widthContent = widthSize - getPaddingLeft() - getPaddingRight();
        int suggestContentSize = Math.min(heightContent, widthContent);
        if (widthMode != MeasureSpec.EXACTLY && heightMode != MeasureSpec.EXACTLY) {
            //宽高皆未指定，使用推荐尺寸
            heightContent = Math.min(DEFAULT_SIZE, heightContent);
            widthContent = Math.min(DEFAULT_SIZE, widthContent);
            suggestContentSize = Math.min(heightContent, widthContent);
            height = suggestContentSize + getPaddingTop() + getPaddingBottom();
            width = suggestContentSize + getPaddingLeft() + getPaddingRight();
        } else if (widthMode != MeasureSpec.EXACTLY) {
            width = suggestContentSize + getPaddingLeft() + getPaddingRight();
        } else if (heightMode != MeasureSpec.EXACTLY) {
            height = suggestContentSize + getPaddingTop() + getPaddingBottom();
        }
        setMeasuredDimension(
                MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
        );
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        drawableArea.set(getPaddingLeft(),
                getPaddingTop(),
                w - getPaddingRight(),
                h - getPaddingBottom()
        );
        updateRing();
        updateProgressShader();
    }

    /**
     * 更新圆环半径与绘制区域
     * 受视图大小、圆环宽度影响
     */
    private void updateRing() {
        ringRadius = Math.min(drawableArea.width(), drawableArea.height()) / 2f - ringWidth / 2f;
        ringDrawRectF.set(
                drawableArea.centerX() - ringRadius,
                drawableArea.centerY() - ringRadius,
                drawableArea.centerX() + ringRadius,
                drawableArea.centerY() + ringRadius
        );
        postInvalidate();
    }

    /**
     * 更新进度条颜色，渐变色根据颜色和当前进度进行变化，所以这两个参数受影响时需要调用此方法更新
     */
    private void updateProgressShader() {
        SweepGradient sweepGradient = new SweepGradient(
                ringDrawRectF.centerX(),
                ringDrawRectF.centerY(),
                new int[]{
                        progressStartColor,
                        progressEndColor},
                new float[]{
                        0,
                        progress * 1f / max
                }
        );
        progressPaint.setShader(sweepGradient);
        postInvalidate();
    }

    private float progressAngle = 0;

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        progressAngle = getCurrAngle();
        //ring bg
        canvas.drawCircle(ringDrawRectF.centerX(), ringDrawRectF.centerY(), ringRadius, ringBackgroundPaint);

        canvas.save();
        canvas.rotate(90, ringDrawRectF.centerX(), ringDrawRectF.centerY());
        //ring progress
        //开始点
        progressStartPath.reset();
        progressStartPath.arcTo(
                ringDrawRectF.right - ringWidth / 2f,
                ringDrawRectF.centerY() - ringWidth / 2f,
                ringDrawRectF.right + ringWidth / 2f,
                ringDrawRectF.centerY() + ringWidth / 2f,
                180,
                180,
                false
        );
        commonPaint.setColor(progressStartColor);
        canvas.drawPath(progressStartPath, commonPaint);

        //进度
        progressPath.reset();
        //注意，这个方法应该是内部会对sweepAngle求余，所以不能>=360
        progressPath.arcTo(ringDrawRectF, 0, Math.min(359.9f, progressAngle));
        canvas.drawPath(progressPath, progressPaint);

        //结束点
        canvas.save();
        canvas.rotate(progressAngle, ringDrawRectF.centerX(), ringDrawRectF.centerY());
        progressEndPath.reset();
        progressEndPath.arcTo(
                ringDrawRectF.right - ringWidth / 2f,
                ringDrawRectF.centerY() - ringWidth / 2f,
                ringDrawRectF.right + ringWidth / 2f,
                ringDrawRectF.centerY() + ringWidth / 2f,
                -5,
                190,//因变换绘制问题导致与progress接壤处有虚线，这里扩大两度
                false
        );
        commonPaint.setColor(progressEndColor);
        canvas.drawPath(progressEndPath, commonPaint);
        commonPaint.setColor(sliderColor);
        canvas.drawCircle(ringDrawRectF.right, ringDrawRectF.centerY(), sliderSize / 2f, commonPaint);
        canvas.restore();
        canvas.restore();
    }

    private final Matrix transformMatrix = new Matrix();

    private void calcEndPos(float cx, float cy, float angle, float x, float y) {
        //就不用三角函数了，直接用矩阵变换转换点位置
        transformMatrix.reset();
        transformMatrix.postRotate(angle, cx, cy);
        float[] result = new float[2];
        transformMatrix.mapPoints(result, new float[]{x, y});
    }

    /**
     * 覆写触摸事件处理，实现根据触摸点更新当前进度
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (mOnSeekBarChangeListener != null) {
                    mOnSeekBarChangeListener.onStartTrackingTouch(this);
                }
                setAngleByPosition(event.getX(), event.getY());
                break;
            case MotionEvent.ACTION_MOVE:
                setAngleByPosition(event.getX(), event.getY());
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                setAngleByPosition(event.getX(), event.getY());
                if (mOnSeekBarChangeListener != null) {
                    mOnSeekBarChangeListener.onStopTrackingTouch(this);
                }
                break;
        }
        return true;
    }

    private void setAngleByPosition(float x, float y) {
        if (ringRadius <= 0) {
            return;
        }
        //用三角函数计算角度，android坐标系为左上角（0，0），往右x增大，往下y增大
        //我们从底部中间位置开始作为0度，将计算分为左右两边
        float cx = ringDrawRectF.centerX();
        float cy = ringDrawRectF.centerY();
        double dy = y - cy;
        double r = Math.sqrt((x - cx) * (x - cx) + (y - cy) * (y - cy));
        double angle = Math.acos(dy / r) * 180.0 / Math.PI;
        if (x > cx) {
            //右半区[180,360]
            //得出的结果取相反数
            angle = -angle;
        }
        angle += 360;//取正
        angle %= 360;//取余
        float ca = getCurrAngle();
        if (angle <= 90) {
            if (ca >= 270 && ca <= 360) {
                angle = 360;
            }
        }
        if (angle >= 270 && angle <= 360) {
            if (ca <= 90) {
                angle = 0;
            }
        }
        setProgress((int) (angle / 360 * max), true);
    }

    private float getCurrAngle() {
        if (max == 0) {
            return 0;
        }
        return progress * 1f / max * 360;
    }

    public void setProgress(int progress) {
        setProgress(progress, false);
    }

    private void setProgress(int progress, boolean fromUser) {
        if (this.progress != progress) {
            this.progress = progress;
            updateProgressShader();
            if (mOnSeekBarChangeListener != null) {
                mOnSeekBarChangeListener.onProgressChanged(this, progress, fromUser);
            }
        }
    }

    public int getProgress() {
        return progress;
    }

    public void setProgressStartColor(int progressStartColor) {
        this.progressStartColor = progressStartColor;
        updateProgressShader();
    }

    public void setProgressEndColor(int progressEndColor) {
        this.progressEndColor = progressEndColor;
        updateProgressShader();
    }

    public void setMax(int max) {
        this.max = max;
        updateProgressShader();
    }

    public int getMax() {
        return max;
    }

    public void setRingWidth(int ringWidth) {
        this.ringWidth = ringWidth;
        ringBackgroundPaint.setStrokeWidth(ringWidth);
        progressPaint.setStrokeWidth(ringWidth);
        updateRing();
    }

    public void setSliderColor(int sliderColor) {
        this.sliderColor = sliderColor;
        postInvalidate();
    }

    public void setSliderSize(int sliderSize) {
        this.sliderSize = sliderSize;
        postInvalidate();
    }

    public void setRingBackgroundColor(int ringBackgroundColor) {
        this.ringBackgroundColor = ringBackgroundColor;
        ringBackgroundPaint.setColor(ringBackgroundColor);
        postInvalidate();
    }

    public void setOnSeekBarChangeListener(OnSeekBarChangeListener listener) {
        mOnSeekBarChangeListener = listener;
    }

    public interface OnSeekBarChangeListener {

        void onProgressChanged(CircleSeekBar seekBar, int progress, boolean fromUser);

        void onStartTrackingTouch(CircleSeekBar seekBar);

        void onStopTrackingTouch(CircleSeekBar seekBar);
    }
}
